test(plugin-charts): pin what the site-2 refusal settles to, not what it is not - #8711
Merged
os-justin merged 2 commits intoSep 9, 2026
Merged
Conversation
… it is not
`ObjectChart.contractEnvelope-6839`'s site-2 refusal arm asserted
`.not.toBe('Apollo')` — a negation over an open codomain. Every value except
that one literal satisfied it, including `String(undefined)`, `''` and `'null'`
— i.e. every value a resolver emits once it has stopped resolving anything.
Ablation on this file: deleting the `|| rawValue` fallback in
`resolveGroupByLabels`, so an unresolved foreign key reaches the axis as the
literal string `undefined`, left all six cases GREEN under the old pin. The
file blessed an implementation strictly worse than the bug it was written to
catch.
The arm now names the one value a correct refusal settles on: `'p1'`, the
foreign key verbatim, which is the raw-FK axis the module docblock already
describes. Measured, not assumed — the lookup branch ends
`idToName[rawValue] || rawValue`, so `humanizeLabel` is NOT on this path and
never uppercases the key. The helper docstring claimed `'P1'`; that claim is
corrected here, since a pin written for it would redden on a correct refusal.
Test-only. No production code changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
…othing Empty frontmatter — the explicit exemption for a test-only change, not a workaround. `scripts/check-changeset-presence.mjs` goes green on it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
os-justin
marked this pull request as ready for review
September 9, 2026 00:12
os-justin
enabled auto-merge
September 9, 2026 00:12
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-justin
deleted the
claude/issue-8708-chart-refusal-assertion-strength
branch
September 9, 2026 01:15
os-justin
pushed a commit
that referenced
this pull request
Sep 9, 2026
… file
The merge before this one took this branch's side of an add/add conflict and
proved that drops no BYTE main carries. It does not prove it drops no CLAIM:
the header main landed makes five, and each needed a verdict against the AST
matcher that now sits underneath it. Four survived the rewrite. One had not.
RESTORED — the two regex modes' strict buckets are NESTED on this tree.
The rewrite kept "the modes are incomparable by construction" (M1) and dropped
the measurement that says the union of the two is just `path`'s bucket. M1 is
about shapes; on this tree ident-only is EMPTY, so a reader could take M1 as a
reason to run both modes and union them, which buys nothing. Re-measured on
both trees rather than copied forward:
at da5e4f6 ident 15 ⊂ path 18 ident-only EMPTY, 3 path-only
with main @ a9bc029 ident 12 ⊂ path 15 ident-only EMPTY, the SAME 3
and the same three `PermissionMatrixEditor` sites are the path-only ones in
both. The companion claim — that the mode choice was never the largest source
of movement — is restored as arithmetic the file can be re-run to check: the
mode separates THREE sites, D1 and D2 separate SIXTEEN (−7/+9, both verified
by diffing the site lists at da5e4f6).
ADDED — R2', the half of D1 that did NOT get repaired.
The window is scoped to the enclosing FUNCTION body. That kills the runaway
half (it can never reach the next `it`) and leaves the truncation half alive,
pointed the other way: a wait hosted in a helper the test awaits covers none of
the CALLER's statements. Forced on three probes — the same cross-recorder read
draws a flag with the wait inline and ZERO with it moved into a helper. Not a
corner: 503 of this tree's 3945 `await waitFor(...)` sites have their window
owned by an inner function, 464 of those a helper. The regex matcher was blind
here too, differently, so this is a surviving blind spot rather than a
regression — the header says which, and says the one shape where the old
matcher's runaway window did reach the read and this one does not.
The banner the reader running the script actually sees said the three error
sources were removed. Two were, plus half of the third; it now says that.
Also recorded: the counts move with the TREE alone. Merging main moved the
population 2776 → 2786 and every strict bucket down (AST 20 → 18, path 18 → 15,
ident 15 → 12) with the matcher byte-identical, because #8707/#8711/#8713 each
anchored a wait. That is claim 4 demonstrating itself.
Unchanged and re-verified: the census stays OUT of CI. No workflow and no
package script runs it; the only caller is the pin test, over six fixtures.
objectui#8704
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 9, 2026
… fixtures a home (objectstack-ai#8719) * test(plugin-designer): anchor the lookupKeying absence pin on the handler's reload `expect(deletes).toEqual([])` was dated to the first PUT by `waitFor(puts)`. That covers the delete scan only because the scan happens to run before the save loop in `handleObjectsChange` — a property of the page, not one the file asserts. Anchor it on `reload()`, the handler's last statement, so the emptiness is read after every write the handler makes. Measured: with a stray `reset` issued after the saves, the `puts` wait leaves this assertion green while C0/H1/H2 in the same file — which already anchor on the reload — go red. objectui#8690 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S * chore(scripts): keep objectui#8690's recorder-wait census as a script The detector answers where to look, never what is wrong, so it is a census tool and NOT wired into CI: a gate on this list would institutionalise the batch repair the card exists to prevent. Two recorder-matching modes, because the choice moves the numbers, and the header records what each one measured on the card's base commit rather than quoting the card's counts as reproduced. Changeset declares an empty frontmatter: test-only plus one repo script, nothing published moves. objectui#8690 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S * chore(scripts): record what the recorder-wait census's two matchers disagree about The census script's numbers move with an internal choice its header stated but never measured. objectui#8703 measured it, and the answer is wider than the mode choice: - The two `--recorder-match` modes are incomparable by construction — each flags a shape the other cannot see (forced on fixtures, both directions). - On da5e4f6 the buckets are nevertheless nested: ident's 15 is a subset of path's 18, ident-only is empty. "They do not contain each other" is an in-principle claim, false as a measurement of this tree. - The header's stated `path` blind spot (pushed bare, read as `host.inits[0]`) is wrong: both modes miss that shape. - Two mode-independent rules dominate the mode choice: the forward window ends at the next `await` in the FILE rather than at the end of the enclosing test, and any textual occurrence counts as a read. Seven of the eighteen strict flags on that tree point at a declaration, a destructuring, a reset or a function parameter — not at a read. Header rewritten to carry that, plus a caveat printed next to the counts. The detector's behaviour is unchanged: both modes print byte-identical site lists. Script and changeset only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S * chore(scripts): give the recorder-wait census an AST matcher, and its fixtures a home objectui#8704. The census resolved a recorder by the SPELLING of its `.push(` site, ended its forward window at the next textual `await` IN THE FILE, and counted every textual occurrence as a read. Seven of the eighteen strict flags it reported at da5e4f6 were therefore a declaration, a destructuring, a reset or a function parameter — and the same truncation went blind on a genuine cross-recorder read one ordinary `await` further on. The default matcher is now an AST pass: - identity over bindings and aliases, not names, one hop through a helper's parameter and a same-file factory's return value; - windows scoped to the enclosing test body, in statements, ending at the next awaited SETTLING anchor rather than at any `await`; - every occurrence classified read / write / declaration. The original census is kept behind `--matcher=regex`, so objectui#8690's and objectui#8703's published numbers stay reproducible from this file rather than being claims about a deleted script. Verified: at da5e4f6 it still prints 159 / 15 strict for `ident` and 167 / 18 strict for `path`, exactly as published. objectui#8703's five throwaway fixtures are committed as the script's test suite, with the sixth objectui#8704 asked for — the absence read objectui#8690 repaired, which must STAY flagged, because "flag nothing" is strictly worse than the bug and passes every other case. Both matchers are pinned over the same six files, so the repair is pinned as a direction. The header's "no count printed here is a corpus fact" caveat STAYS, with its residuals restated: no type checker, a window rule that is a judgement rather than a fact, and a flag that is still a site to read and not a defect. The census remains out of CI; the pin test runs the matcher over the fixtures only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S * chore(scripts): resolve the census header claim by claim, not file by file The merge before this one took this branch's side of an add/add conflict and proved that drops no BYTE main carries. It does not prove it drops no CLAIM: the header main landed makes five, and each needed a verdict against the AST matcher that now sits underneath it. Four survived the rewrite. One had not. RESTORED — the two regex modes' strict buckets are NESTED on this tree. The rewrite kept "the modes are incomparable by construction" (M1) and dropped the measurement that says the union of the two is just `path`'s bucket. M1 is about shapes; on this tree ident-only is EMPTY, so a reader could take M1 as a reason to run both modes and union them, which buys nothing. Re-measured on both trees rather than copied forward: at da5e4f6 ident 15 ⊂ path 18 ident-only EMPTY, 3 path-only with main @ a9bc029 ident 12 ⊂ path 15 ident-only EMPTY, the SAME 3 and the same three `PermissionMatrixEditor` sites are the path-only ones in both. The companion claim — that the mode choice was never the largest source of movement — is restored as arithmetic the file can be re-run to check: the mode separates THREE sites, D1 and D2 separate SIXTEEN (−7/+9, both verified by diffing the site lists at da5e4f6). ADDED — R2', the half of D1 that did NOT get repaired. The window is scoped to the enclosing FUNCTION body. That kills the runaway half (it can never reach the next `it`) and leaves the truncation half alive, pointed the other way: a wait hosted in a helper the test awaits covers none of the CALLER's statements. Forced on three probes — the same cross-recorder read draws a flag with the wait inline and ZERO with it moved into a helper. Not a corner: 503 of this tree's 3945 `await waitFor(...)` sites have their window owned by an inner function, 464 of those a helper. The regex matcher was blind here too, differently, so this is a surviving blind spot rather than a regression — the header says which, and says the one shape where the old matcher's runaway window did reach the read and this one does not. The banner the reader running the script actually sees said the three error sources were removed. Two were, plus half of the third; it now says that. Also recorded: the counts move with the TREE alone. Merging main moved the population 2776 → 2786 and every strict bucket down (AST 20 → 18, path 18 → 15, ident 15 → 12) with the matcher byte-identical, because objectstack-ai#8707/objectstack-ai#8711/objectstack-ai#8713 each anchored a wait. That is claim 4 demonstrating itself. Unchanged and re-verified: the census stays OUT of CI. No workflow and no package script runs it; the only caller is the pin test, over six fixtures. objectui#8704 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8708
What was wrong
ObjectChart.contractEnvelope-6839's site-2 refusal arm asserted.not.toBe('Apollo')— a negation over an open codomain. Every value except that one literal satisfied it:String(undefined),'','null', and every other value a resolver emits once it has stopped resolving anything.Leg 1 — the vacuity, demonstrated rather than argued
Two read-site mutations were run against the pre-fix file. Both mutated
resolveGroupByLabelsinpackages/plugin-charts/src/ObjectChart.tsx, never the pin; both ran from a committed tree withtraprestore and were proved on disk by anchor counts,git hash-objectagainst the HEAD blob, and a line-total gate; both restored by state (git diff HEADempty).Mutation A — "the read returns nothing, ever" (
extractRecords(results)becomes[]; on-diskcb9934d7to252cc4ad):datarecords(the refusal arm)The refusal arm passes while the resolver resolves nothing at all. But this mutation is NEGATIVE, not a discriminator: the new pin passes under it too (re-run and confirmed, 4 passed / 2 failed, identical classification). That is structural — an arm asserting "this envelope resolves nothing" cannot, on its own, redden for an implementation that resolves nothing for everything. What catches mutation A is the file's two positive arms, not the refusal arm.
Mutation B — the sharp one (drop the
|| rawValuefallback, so an unresolved foreign key reaches the axis as the literal stringundefined; on-diskcb9934d7to4e1f6df5):All six cases GREEN. The file blessed a component that renders
undefinedon the axis for every unresolved lookup key — an implementation strictly worse than the bug the module docblock says every case must refuse.The repair — assert the positive
'p1', measured — not'P1'. The card and the file's own helper docstring both claimedhumanizeLabeluppercases the unresolved key. It does not: the lookup branch ends[groupByField]: idToName[rawValue] || rawValue, andhumanizeLabelis not on that path at all. Probed directly (a deliberatetoBe('___PROBE___')read, restored by state):expected 'p1' to be '___PROBE___'. A pin written for'P1'would have reddened on a correct refusal. The docstring is corrected here for the same reason.'p1'is not an accidental value — it is the raw-foreign-key axis the module docblock already describes as the silent-looking failure.Legs 2-4
AssertionError: expected 'undefined' to be 'p1'— exactly theString(undefined)family the card names.4e1f6df5, asserted equal to legs 1b/2), the pre-fix pin restored from the base blob —git checkout da5e4f69e -- FILE, on-diskgit hash-object=c3d2540f8d44300a65e7c50e4d3ed284e787eaa9=git rev-parse da5e4f69e:FILE, anchor counts.not.toBe('Apollo')= 1 and.toBe('p1')= 0 — passes 6/6. A strengthening, not a relocation.The first leg-4 attempt was VOID (harness argument bug,
exit 127, no JSON emitted) and is reported as void, not green; the run above is the fixed re-run.Every verdict above is per-test classification from vitest's JSON reporter, never the text reporter.
Population sweep — reported, not batch-repaired
Across the seven
contractEnvelope-6839siblings, negated value matchers (.not.toBe/.not.toEqual/.not.toStrictEqual): charts 1, the other six 0. Control LIT — the same grep against this file's base blob finds the known.not.toBe('Apollo')at line 191, so the six zeros are readings.Repo-wide the same shape occurs 541 times in
packages/**test files. That is a pointer, not a diagnosis, and it is left untouched: most uses are legitimate (asserting two values differ), and telling those apart needs a read per site.Scope and verification
ObjectChart.tsxwas mutated for ablation and restored by state each time.pnpm exec vitest run packages/plugin-charts/— 52 files, 488 tests, all passed.pnpm --filter @object-ui/plugin-charts run type-check— green, with the dependency closure built. Coverage measured, not assumed:tsc -p tsconfig.test.json --listFilescounts this test file 1, so the type-check really sees it.node scripts/check-changeset-presence.mjsgreen on an empty-frontmatter changeset (the explicit "releases nothing" exemption;skip-changesetis a phantom label here).check-changeset-no-majorgreen.check-control-bytesgreen, plus a direct control-byte scan of both changed files.node scripts/check-governed-queue-guard.mjs --test— NOT GOVERNED, 2 paths against 5 surfaces.5f18ba861. No overlap with objectui#8709 (plugin-map/plugin-timeline), objectui#8703 / PR chore(scripts): record what the recorder-wait census's two matchers disagree about #8706 (scripts/), or the PR feat(app-shell,i18n): announce inbox arrivals with a toast and a desktop notification (#7011) #8668app-shellCI repair.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code